home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload Trio 2
/
Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO
/
dir24
/
psi110g.zip
/
RIP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-04-17
|
37KB
|
1,156 lines
/* This file contains code to implement the Routing Information Protocol (RIP)
* and is derived from 4.2BSD code. Mike Karels of Berkeley has stated on
* TCP-IP that the code may be freely used as long as UC Berkeley is
* credited. (Well, here's some credit :-). AGB 4-28-88
* Further documentation on the RIP protocol is now available in Charles
* Hedrick's draft RFC, as yet unnumbered. AGB 5-6-88
*
* The RIP RFC has now been issued as RFC1058. AGB 7-23-88
*
* Code gutted and substantially rewritten. KA9Q 9/89
*
* Mods by PA0GRI
*
* Changes Copyright (c) 1993 Jeff White - N0POY, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*
* Rehack for RIP-2 (RFC1388) by N0POY 4/1993
* Modules needed for changes: rip.c, rip.h, ripcmd.c, ripdump.c, ip.h
* commands.h, iface.h, iface.c, version.c
*
* Beta release 11/16/93 V0.95
*
* Bug fix that prevented split horizon routing to work fixed.
* 2/19/94 release V1.0
*
*/
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include "global.h"
#ifdef RIP
#include "mbuf.h"
#include "netuser.h"
#include "udp.h"
#include "timer.h"
#include "iface.h"
#include "ip.h"
#include "internet.h"
#include "rip.h"
#include "arp.h"
#include "socket.h"
struct rip_stat Rip_stat;
int16 Rip_trace;
FILE *Rip_trace_file = NULLFILE;
char *Rip_trace_fname = NULLCHAR;
int16 Rip_merge;
int32 Rip_ttl = RIP_TTL;
int16 Rip_ver_refuse = 0;
int16 Rip_default_refuse = FALSE;
struct rip_list *Rip_list;
struct udp_cb *Rip_cb;
struct rip_auth *Rip_auth;
struct rip_refuse *Rip_refuse;
char Rip_nullpass[RIP_AUTH_SIZE];
static void rip_rx __ARGS((struct iface *iface,struct udp_cb *sock,int cnt));
static void proc_rip __ARGS((struct iface *iface,int32 gateway,
struct rip_route *ep,unsigned char version));
static char *putheader __ARGS((char *cp,char command,char version,int16 domain));
static char *putentry __ARGS((char *cp,int16 fam,int16 tag,int32 target,
int32 targmask,int32 router,int32 metric));
static void send_routes __ARGS((int32 dest,int16 port,int trig,
int flags,char version, struct rip_list *rdata));
static void pullauthentication __ARGS((struct rip_authenticate *ep, struct mbuf **bpp));
static void pullheader __ARGS((struct rip_head *ep, struct mbuf **bpp));
static int check_authentication __ARGS((struct rip_auth *auth,
struct mbuf **bpp, struct rip_head *header, int32 srcaddr, char *ifcname,
struct rip_authenticate *entry));
static char *putauth __ARGS((char *cp, int16 authtype,
char *authpass));
static void rip_trace(short level, char *errstr, ...);
/* Send RIP CMD_RESPONSE packet(s) to the specified rip_list entry */
void
rip_shout(p)
void *p;
{
register struct rip_list *rl;
rl = (struct rip_list *)p;
stop_timer(&rl->rip_time);
send_routes(rl->dest,RIP_PORT,0,rl->flags,rl->rip_version,rl);
set_timer(&rl->rip_time,rl->interval*1000L);
start_timer(&rl->rip_time);
}
/* Send the routing table. */
static void
send_routes(dest,port,trig,flags,version,rdata)
int32 dest; /* IP destination address to send to */
int16 port;
int trig; /* Send only triggered updates? */
int flags;
char version; /* Version of RIP packet */
struct rip_list *rdata; /* Used for RIP-2 packets */
{
char *cp;
int i,bits,numroutes,maxroutes;
int16 pktsize;
struct mbuf *bp;
struct route *rp;
struct socket lsock,fsock;
struct iface *iface;
if((rp = rt_lookup(dest)) == NULLROUTE) {
rip_trace(1, "No route to [%s] exists, cannot send", inet_ntoa(dest));
return; /* No route exists, can't do it */
}
iface = rp->iface;
/* Compute maximum packet size and number of routes we can send */
pktsize = ip_mtu(dest) - IPLEN;
pktsize = min(pktsize,RIP_PKTSIZE);
maxroutes = (pktsize - RIP_HEADER) / RIP_ENTRY;
lsock.address = INADDR_ANY;
lsock.port = RIP_PORT;
fsock.address = dest;
fsock.port = port;
/* Allocate space for a full size RIP packet and generate header */
if((bp = alloc_mbuf(pktsize)) == NULLBUF)
return;
numroutes = 0;
/* See if we know information about what to send out */
if ((version >= RIP_VERSION_2) && (rdata != NULLRL)) {
cp = putheader(bp->data,RIPCMD_RESPONSE, version, rdata->domain);
/* See if we need to put an authentication header on */
if (flags & RIP_AUTHENTICATE) {
cp = putauth(cp, RIP_AUTH_SIMPLE, rdata->rip_auth_code);
numroutes++;
}
} else {
cp = putheader(bp->data,RIPCMD_RESPONSE, version, 0);
}
/* Emit route to ourselves, if requested */
if(flags & RIP_US) {
if (version == RIP_VERSION_1)
cp = putentry(cp,RIP_AF_INET,0,iface->addr,0,0,1);
else
cp = putentry(cp,RIP_AF_INET,0,iface->addr,0xFFFFFFFFL,0,1);
numroutes++;
}
/* Emit default route, if appropriate */
if(R_default.iface != NULLIF && !(R_default.flags & RTPRIVATE)
&& (!trig || (R_default.flags & RTTRIG))){
if(!(flags & RIP_SPLIT) || iface != R_default.iface){
if (version == RIP_VERSION_1)
cp = putentry(cp,RIP_AF_INET,0,0,0,0,R_default.metric);
else
cp = putentry(cp,RIP_AF_INET,R_default.route_tag,0,0,0,R_default.metric);
numroutes++;
} else if (trig && (flags & RIP_POISON)) {
/* Poisoned reverse */
if (version == RIP_VERSION_1)
cp = putentry(cp,RIP_AF_INET,0,0,0,0,RIP_METRIC_UNREACHABLE);
else
cp = putentry(cp,RIP_AF_INET,R_default.route_tag,0,0,0,
RIP_METRIC_UNREACHABLE);
numroutes++;
}
}
for(bits=0;bits<32;bits++){
for(i=0;i<HASHMOD;i++){
for(rp = Routes[bits][i];rp != NULLROUTE;rp=rp->next){
if((rp->flags & RTPRIVATE)
|| (trig && !(rp->flags & RTTRIG)))
continue;
if(numroutes >= maxroutes){
/* Packet full, flush and make another */
bp->cnt = RIP_HEADER + numroutes * RIP_ENTRY;
send_udp(&lsock,&fsock,0,0,bp,bp->cnt,0,0);
Rip_stat.vdata[version].output++;
if((bp = alloc_mbuf(pktsize)) == NULLBUF)
return;
numroutes = 0;
if (version >= RIP_VERSION_2 && rdata != NULLRL) {
cp = putheader(bp->data,RIPCMD_RESPONSE, version,
rdata->domain);
/* See if we need to put an authentication header on */
if (flags & RIP_AUTHENTICATE) {
cp = putauth(cp, RIP_AUTH_SIMPLE, rdata->rip_auth_code);
numroutes++;
}
} else {
cp = putheader(bp->data,RIPCMD_RESPONSE, version, 0);
}
}
if(!(flags & RIP_SPLIT) || iface != rp->iface){
if (version == RIP_VERSION_1)
cp = putentry(cp,RIP_AF_INET,0,rp->target,0,0,rp->metric);
else
cp = putentry(cp,RIP_AF_INET,rp->route_tag,rp->target,
(0xFFFFFFFFL << (32 - rp->bits)),rdata?rdata->proxy_route:0,
rp->metric);
numroutes++;
} else if(trig && (flags & RIP_POISON)) {
if (version == RIP_VERSION_1)
cp = putentry(cp,RIP_AF_INET,0,rp->target,0,0,RIP_METRIC_UNREACHABLE);
else
cp = putentry(cp,RIP_AF_INET,rp->route_tag,rp->target,
(0xFFFFFFFFL << (32 - rp->bits)),rdata?rdata->proxy_route:0,
RIP_METRIC_UNREACHABLE);
numroutes++;
}
}
}
}
if(numroutes != 0){
bp->cnt = RIP_HEADER + numroutes * RIP_ENTRY;
send_udp(&lsock,&fsock,0,0,bp,bp->cnt,0,0);
Rip_stat.vdata[version].output++;
} else {
free_p(bp);
}
}
/* Add an entry to the rip broadcast list */
int
rip_add(dest,interval,flags,version,authpass,domain,route_tag,proxy)
int32 dest;
int32 interval;
char flags;
char version;
char authpass[RIP_AUTH_SIZE];
int16 domain;
int16 route_tag;
int32 proxy;
{
register struct rip_list *rl;
struct route *rp;
for(rl = Rip_list; rl != NULLRL; rl = rl->next)
if((rl->dest == dest) && (rl->domain == domain))
return 0;
if((rp = rt_lookup(dest)) == NULLROUTE){
tprintf("%s is unreachable\n",inet_ntoa(dest));
return 0;
}
/* get a chunk of memory for the rip interface descriptor */
rl = (struct rip_list *)callocw(1,sizeof(struct rip_list));
/* tack this record on as the first in the list */
rl->next = Rip_list;
if(rl->next != NULLRL)
rl->next->prev = rl;
Rip_list = rl;
rl->dest = dest;
rip_trace(9, "Rip added V%d Flags %d Tag %d Proxy %s Domain %d Auth %s Interval %d",
version, flags, route_tag, inet_ntoa(proxy), domain, authpass, interval);
/* and the interface ptr, tick interval and flags */
rl->iface = rp->iface;
rl->rip_version = version;
rl->interval = interval;
rl->flags = flags;
rl->proxy_route = proxy;
rl->route_tag = route_tag;
rl->domain = domain;
memcpy(rl->rip_auth_code,authpass,RIP_AUTH_SIZE);
/* and set up the timer stuff */
set_timer(&rl->rip_time,interval*1000L);
rl->rip_time.func = rip_shout;
rl->rip_time.arg = rl;
start_timer(&rl->rip_time);
return 1;
}
/* add a gateway to the rip_refuse list which allows us to ignore their
* advertisements
*/
int
riprefadd(gateway)
int32 gateway;
{
register struct rip_refuse *rl;
for(rl = Rip_refuse; rl != NULLREF; rl = rl->next)
if(rl->target == gateway)
return 0; /* Already in table */
/* get a chunk of memory for the rip interface descriptor */
rl = (struct rip_refuse *)callocw(1,sizeof(struct rip_refuse));
/* tack this record on as the first in the list */
rl->next = Rip_refuse;
if(rl->next != NULLREF)
rl->next->prev = rl;
Rip_refuse = rl;
/* fill in the gateway to ignore */
rl->target = gateway;
return 0;
}
/* Add an authentication type to an interface name */
int
ripauthadd(ifcname, domain, password)
char *ifcname;
int16 domain;
char *password;
{
register struct rip_auth *ra;
char *name;
int x;
for(ra = Rip_auth; ra != NULLAUTH; ra = ra->next)
if(!strcmp(ifcname,ra->ifc_name) && (ra->domain == domain))
return 1; /* Already in table */
/* get a chunk of memory for the rip interface descriptor */
ra = (struct rip_auth *)callocw(1,sizeof(struct rip_auth));
/* tack this record on as the first in the list */
ra->next = Rip_auth;
if(ra->next != NULLAUTH)
ra->next->prev = ra;
Rip_auth = ra;
/* fill in the data */
ra->ifc_name = mallocw(strlen(ifcname)+1);
strcpy(ra->ifc_name, ifcname);
ra->domain = domain;
for (x = 0; x < RIP_AUTH_SIZE+1; x++)
ra->rip_auth_code[x] = '\0';
strcpy(ra->rip_auth_code, password);
return 0;
}
/* Drop an authentication to an interface name */
int
ripauthdrop(ifcname, domain)
char *ifcname;
int16 domain;
{
register struct rip_auth *ra;
char *name;
for(ra = Rip_auth; ra != NULLAUTH; ra = ra->next)
if(!strcmp(ifcname,ra->ifc_name) && (ra->domain == domain))
break;
/* leave if we didn't find it */
if(ra == NULLAUTH)
return 0;
/* Unlink from list */
if(ra->next != NULLAUTH)
ra->next->prev = ra->prev;
if(ra->prev != NULLAUTH)
ra->prev->next = ra->next;
else
Rip_auth = ra->next;
free((char *)ra->ifc_name);
free((char *)ra);
return 0;
}
/* drop a RIP target */
int
rip_drop(dest,domain)
int32 dest;
int16 domain;
{
register struct rip_list *rl;
for(rl = Rip_list; rl != NULLRL; rl = rl->next)
if((rl->dest == dest) && (rl->domain == domain))
break;
/* leave if we didn't find it */
if(rl == NULLRL)
return 0;
/* stop the timer */
stop_timer(&rl->rip_time);
/* Unlink from list */
if(rl->next != NULLRL)
rl->next->prev = rl->prev;
if(rl->prev != NULLRL)
rl->prev->next = rl->next;
else
Rip_list = rl->next;
/* and deallocate the descriptor memory */
free((char *)rl);
return 0;
}
/* drop a RIP-refuse target from the rip_refuse list */
int
riprefdrop(gateway)
int32 gateway;
{
register struct rip_refuse *rl;
for(rl = Rip_refuse; rl != NULLREF; rl = rl->next)
if(rl->target == gateway)
break;
/* leave if we didn't find it */
if(rl == NULLREF)
return 0;
/* Unlink from list */
if(rl->next != NULLREF)
rl->next->prev = rl->prev;
if(rl->prev != NULLREF)
rl->prev->next = rl->next;
else
Rip_refuse = rl->next;
/* and deallocate the structure memory */
free((char *)rl);
return 0;
}
/* function to output a RIP CMD_RESPONSE packet for the rip_trigger list */
void
rip_trigger()
{
register struct rip_list *rl;
int bits,i;
struct route *rp;
for(rl=Rip_list;rl != NULLRL;rl = rl->next){
send_routes(rl->dest,RIP_PORT,1,rl->flags,rl->rip_version,rl);
}
/* Clear the trigger list */
R_default.flags &= ~RTTRIG;
for(bits=0;bits<32;bits++){
for(i=0;i<HASHMOD;i++){
for(rp = Routes[bits][i];rp != NULLROUTE;rp = rp->next){
rp->flags &= ~RTTRIG;
}
}
}
}
/* Start RIP agent listening at local RIP UDP port */
int
rip_init()
{
struct socket lsock;
int x;
lsock.address = INADDR_ANY;
lsock.port = RIP_PORT;
if(Rip_cb == NULLUDP)
Rip_cb = open_udp(&lsock,rip_rx);
for (x = 0; x < RIP_AUTH_SIZE; x++)
Rip_nullpass[x] = 0;
Rip_trace = 0;
/* Add the 0 domain with no password */
ripauthadd(DEFAULTIFC, 0, RIP_NO_AUTH);
return 0;
}
/* Process RIP input received from 'interface'. */
static void
rip_rx(iface,sock,cnt)
struct iface *iface;
struct udp_cb *sock;
int cnt;
{
struct mbuf *bp;
struct socket fsock;
struct rip_refuse *rfl;
struct rip_route entry;
struct rip_head header;
struct route *rp;
struct rip_list *rl;
/* receive the RIP packet */
recv_udp(sock,&fsock,&bp);
/* check the gateway of this packet against the rip_refuse list and
* discard it if a match is found
*/
for(rfl=Rip_refuse;rfl != NULLREF;rfl = rfl->next){
if(fsock.address == rfl->target){
Rip_stat.refusals++;
rip_trace(2, "RIP refused from %s",inet_ntoa(fsock.address));
free_p(bp);
return;
}
}
pullheader(&header, &bp);
/* increment the rcvd cnt */
Rip_stat.vdata[header.rip_vers].rcvd++;
/* Check to see if we'll accept this version on this interface */
if (header.rip_vers <= Rip_ver_refuse) {
rip_trace(3, "RIP version %d refused from [%s]", header.rip_vers,
inet_ntoa(fsock.address));
Rip_stat.refusals++;
free_p(bp);
return;
}
/* Check the version of the frame */
switch (header.rip_vers) {
case RIP_VERSION_2 :
break;
case RIP_VERSION_0 :
rip_trace(1, "RIP Version 0 refused from [%s]", inet_ntoa(fsock.address));
Rip_stat.version++;
free_p(bp);
return;
case RIP_VERSION_1 :
/* Toss RIP if header is bogus for V1 */
if (header.rip_domain != 0) {
rip_trace(1, "RIP-1 bogus header, data in null fields from [%s]",
inet_ntoa(fsock.address));
Rip_stat.vdata[RIP_VERSION_1].unknown++;
free_p(bp);
return;
}
break;
}
rip_trace(2, "RIP Packet version %d processing",header.rip_vers);
switch(header.rip_cmd){
case RIPCMD_RESPONSE:
rip_trace(2, "RIPCMD_RESPONSE from [%s] domain %d",inet_ntoa(fsock.address),
header.rip_domain);
Rip_stat.vdata[header.rip_vers].response++;
pullentry(&entry,&bp);
if (header.rip_vers >= RIP_VERSION_2) {
/* We still have the authentication entry from above */
if (!check_authentication(Rip_auth, &bp, &header,fsock.address,
iface->name, (struct rip_authenticate *)&entry)) {
free_p(bp);
return;
}
}
proc_rip(iface,fsock.address,&entry,header.rip_vers);
while(len_p(bp) >= RIP_ENTRY){
pullentry(&entry,&bp);
proc_rip(iface,fsock.address,&entry,header.rip_vers);
}
/* If we can't reach the sender of this update, or if
* our existing route is not through the interface we
* got this update on, add him as a host specific entry
*/
if((rp = rt_blookup(fsock.address,32)) != NULLROUTE){
/* Host-specific route already exists, refresh it */
start_timer(&rp->timer);
} else if((rp = rt_lookup(fsock.address)) == NULLROUTE
|| rp->iface != iface) {
entry.rip_family = RIP_AF_INET;
entry.rip_tag = 0;
entry.rip_dest = fsock.address;
if (header.rip_vers > RIP_VERSION_1)
entry.rip_dest_mask = 32;
else
entry.rip_dest_mask = 0;
entry.rip_router = 0;
entry.rip_metric = 0; /* will get incremented to 1 */
proc_rip(iface,fsock.address,&entry,header.rip_vers);
}
if(Rip_merge)
rt_merge(Rip_trace);
rip_trigger();
break;
case RIPCMD_REQUEST:
rip_trace(2, "RIPCMD_REQUEST from [%s] domain %d",inet_ntoa(fsock.address),
header.rip_domain);
Rip_stat.vdata[header.rip_vers].request++;
/* For now, just send the whole table with split horizon
* enabled when the source port is RIP_PORT, and send
* the whole table with split horizon disable when another
* source port is used. This should be replaced with a more
* complete implementation that checks for non-global requests
*/
if (header.rip_vers > RIP_VERSION_1) {
/* RIP-2, let's see if we know something about this guy */
rp = rt_lookup(fsock.address);
for (rl = Rip_list; rl != NULLRL; rl = rl->next)
if ((rl->dest == rp->target) && (rl->domain == header.rip_domain))
break;
if (rl == NULLRL)
if (fsock.port == RIP_PORT)
send_routes(fsock.address,fsock.port,0,(RIP_BROADCAST & RIP_SPLIT &
RIP_POISON),header.rip_vers,NULLRL);
else
send_routes(fsock.address,fsock.port,0,(RIP_BROADCAST),
header.rip_vers,NULLRL);
else
if (fsock.port == RIP_PORT)
send_routes(fsock.address,fsock.port,0,rl->flags,header.rip_vers,rl);
else
send_routes(fsock.address,fsock.port,0,(rl->flags & ~(RIP_SPLIT | RIP_POISON)),
header.rip_vers,rl);
} else {
if(fsock.port == RIP_PORT)
send_routes(fsock.address,fsock.port,0,(RIP_SPLIT & RIP_BROADCAST & RIP_POISON),
header.rip_vers,NULLRL);
else
send_routes(fsock.address,fsock.port,0,RIP_BROADCAST,
header.rip_vers,NULLRL);
}
break;
default:
rip_trace(1, "RIPCMD Unknown or not implemented from [%s] command %d",
inet_ntoa(fsock.address),header.rip_cmd);
Rip_stat.vdata[header.rip_vers].unknown++;
break;
} /* switch */
free_p(bp);
}
/* Apply a set of heuristics for determining the number of significant bits
* (i.e., the address mask) in the target address. Needed since RIP doesn't
* include the address mask for each entry. Applies only to RIP-1 packets.
*/
int
nbits(target)
int32 target;
{
int bits;
if(target == 0)
return 0; /* Special case: 0.0.0.0 is the default route */
/* Check the host-part bytes of
* the address to check for byte-wide zeros
* which we'll consider to be subnet routes.
* e.g. 44.80.0.0 will be considered to be equal to 44.80/16
* whereas 44.80.1.0 will be considered to be 44.80.1/24
*/
switch (hibyte(hiword(target)) >> 6) {
case 3: /* Class C address */
/*is it a host address ? i.e. are there any 1's in the
* host part ?
*/
if(target & 0xff)
bits = 32;
else
bits = 24;
break;
case 2: /* Class B address */
if(target & 0xff)
bits = 32;
else if(target & 0xff00)
bits = 24;
else
bits = 16;
break;
case 0: /* Class A address */
case 1:
if(target & 0xff)
bits = 32;
else if(target & 0xff00)
bits = 24;
else if(target & 0xff0000L)
bits = 16;
else
bits = 8;
}
return bits;
}
/* Remove and process a RIP response entry from a packet */
static void
proc_rip(iface,gateway,ep,version)
struct iface *iface;
int32 gateway;
register struct rip_route *ep;
unsigned char version;
{
int32 interval;
int32 target;
unsigned int bits;
register struct route *rp;
struct rip_list *rl;
int add = 0; /* action flags */
int drop = 0;
int trigger = 0;
if(ep->rip_family != RIP_AF_INET) {
if (ep->rip_family == RIP_AF_AUTH)
return;
/* Skip non-IP addresses */
rip_trace(1, "RIP_rx: Not an IP family packet\n");
Rip_stat.addr_family++;
return;
}
/* RIP-1 says all unused fields must be zero */
if (version == RIP_VERSION_1) {
if (ep->rip_tag != 0 || ep->rip_dest_mask != 0 || ep->rip_router != 0) {
rip_trace(1,"RIP_rx: RIP-1 entry bad, data in null fields");
Rip_stat.vdata[version].unknown++;
}
/* Guess at the mask, since it's not explicit for RIP-1 */
bits = nbits(ep->rip_dest);
target = ep->rip_dest;
} else {
/* Assume RIP-2 */
if (!ep->rip_dest_mask) {
/* No netmask, guess */
bits = nbits(ep->rip_dest);
} else {
bits = mask2width(ep->rip_dest_mask);
}
target = ep->rip_dest;
/* Check for "proxy" rip */
if (ep->rip_router) {
rip_trace(3, "Proxy rip pointing to [%s]", inet_ntoa(ep->rip_router));
gateway = ep->rip_router;
}
}
/* Don't ever add a route to myself through somebody! */
if(bits == 32 && ismyaddr(target) != NULLIF) {
rip_trace(2, "Route to self [%s]/32 metric %lu", inet_ntoa(target),
ep->rip_metric);
return;
}
/* Check to see if we'll take a default route, zero bits mean default */
if (Rip_default_refuse && bits == 0) {
rip_trace(2, "Default route refused from [%s]", inet_ntoa(target));
return;
}
/* Update metric to reflect link cost */
ep->rip_metric++;
ep->rip_metric = min(ep->rip_metric,RIP_METRIC_UNREACHABLE);
/* Find existing entry, if any */
rp = rt_blookup(target,bits);
/* Don't touch private routes */
if(rp != NULLROUTE && (rp->flags & RTPRIVATE)) {
rip_trace(3, "Route to [%s]/%u unchanged, private", inet_ntoa(target),
bits);
return;
}
if(rp == NULLROUTE) {
if(ep->rip_metric < RIP_METRIC_UNREACHABLE) {
/* New route; add it and trigger an update */
add++;
trigger++;
}
} else if(rp->metric == RIP_METRIC_UNREACHABLE) {
/* Route is in hold-down; ignore this guy */
rip_trace(2, "Route to [%s]/%u ignored (hold-down) metric %lu",
inet_ntoa(target), bits, ep->rip_metric);
} else if(rp->gateway == gateway && rp->iface == iface) {
/* This is the gateway for the entry we already have;
* restart the timer
*/
start_timer(&rp->timer);
if(rp->metric != ep->rip_metric) {
/* Metric has changed. Update it and trigger an
* update. If route has become unavailable, start
* the hold-down timeout.
*/
rip_trace(3, "Metric change [%s]/%u %lu -> %lu", inet_ntoa(target),
bits, rp->metric, ep->rip_metric);
if(ep->rip_metric == RIP_METRIC_UNREACHABLE)
rt_timeout(rp); /* Enter hold-down timeout */
else
rp->metric = ep->rip_metric;
trigger++;
}
} else {
/* Entry is from a different gateway than the current route */
if(ep->rip_metric < rp->metric) {
/* Switch to a new gateway */
rip_trace(3, "Metric better [%s]/%u new: %lu old: %lu", inet_ntoa(target),
bits, ep->rip_metric, rp->metric);
drop++;
add++;
trigger++;
} else {
/* Metric is no better, stay with current route */
rip_trace(3, "Metric not better [%s]/%u new: %lu old: %lu", inet_ntoa(target),
bits, ep->rip_metric, rp->metric);
}
}
if(drop) {
/* Switching to a better gateway; delete old entry */
rip_trace(2, "Route drop [%s]/%u", inet_ntoa(target), bits);
rt_drop(target,bits);
}
if(add) {
/* Add a new entry */
interval = Rip_ttl;
for(rl=Rip_list; rl != NULLRL; rl = rl->next){
if(rl->iface == iface){
interval = rl->interval * 4;
break;
}
}
rip_trace(2, "Route add [%s]/%u through %s via ",
inet_ntoa(target), bits, iface->name);
rip_trace(2, "[%s] metric %lu", inet_ntoa(gateway),
ep->rip_metric);
rp = rt_add(target,(unsigned) bits,gateway,iface,
(int) ep->rip_metric,interval,0);
/* Add in the routing tag for RIP-2 */
if (version >= RIP_VERSION_2)
rp->route_tag = ep->rip_tag;
else
rp->route_tag = 0;
}
/* If the route changed, mark it for a triggered update */
if(trigger){
rp->flags |= RTTRIG;
}
}
/* Send a RIP request packet to the specified destination */
int
ripreq(dest,replyport,version)
int32 dest;
int16 replyport;
int16 version;
{
struct mbuf *bp;
struct socket lsock,fsock;
char *cp;
register struct rip_list *rl;
lsock.address = INADDR_ANY;
lsock.port = replyport;
/* if we were given a valid dest addr, ask it (the routers on that net)
* for a default gateway
*/
if(dest == 0)
return 0;
fsock.address = dest;
fsock.port = RIP_PORT;
/* Send out one RIP Request packet as a broadcast to 'dest' */
if((bp = alloc_mbuf(RIP_HEADER + RIP_ENTRY)) == NULLBUF)
return -1;
/* Check to see if we already know something about who we're */
/* requesting the RIP from */
for (rl = Rip_list; rl != NULLRL; rl = rl->next)
if (rl->dest == dest)
break;
bp->cnt = RIP_HEADER + RIP_ENTRY;
if (rl != NULLRL) {
cp = putheader(bp->data,RIPCMD_REQUEST,rl->rip_version, rl->domain);
if (rl->rip_version >= RIP_VERSION_2) {
if (rl->flags & RIP_AUTHENTICATE) {
cp = putauth(cp,RIP_AUTH_SIMPLE,rl->rip_auth_code);
bp->cnt += RIP_ENTRY;
}
}
Rip_stat.vdata[rl->rip_version].output++;
} else {
cp = putheader(bp->data,RIPCMD_REQUEST,version, 0);
Rip_stat.vdata[version].output++;
}
cp = putentry(cp,0,0,0L,0L,0L,RIP_METRIC_UNREACHABLE);
send_udp(&lsock, &fsock,0,0,bp,bp->cnt,0,0);
return 0;
}
/* Write the authentication packet */
static char *
putauth(cp,authtype,authpass)
register char *cp;
int16 authtype;
char *authpass;
{
int x;
cp = put16(cp, 0xFFFF);
cp = put16(cp, authtype);
/* Put the password in big-endian (network) byte order */
/* This probably is not the best way to do this, since it */
/* would hose up on a real big-endian machine. Oh well */
/* Something to fix in the future. Whip me, beat me, make */
/* me use an Intel micro brain. -N0POY */
for (x = 0; x < RIP_AUTH_SIZE; x += 4) {
*cp++ = authpass[x+3];
*cp++ = authpass[x+2];
*cp++ = authpass[x+1];
*cp++ = authpass[x];
}
return(cp);
}
/* Write the header of a RIP packet */
static char *
putheader(cp,command,version,domain)
register char *cp;
char command;
char version;
int16 domain;
{
*cp++ = command;
*cp++ = version;
return put16(cp,domain);
}
/* Write a single entry into a rip packet */
static char *
putentry(cp,fam,tag,target,targmask,router,metric)
register char *cp;
int16 fam;
int16 tag;
int32 target;
int32 targmask;
int32 router;
int32 metric;
{
cp = put16(cp,fam);
cp = put16(cp,tag);
cp = put32(cp,target);
cp = put32(cp,targmask);
cp = put32(cp,router);
return put32(cp,metric);
}
/* Check the authentication of RIP-II packets */
int
check_authentication(auth,bpp,header,srcaddr,ifcname,entry)
struct rip_auth *auth;
struct mbuf **bpp;
struct rip_head *header;
int32 srcaddr;
char *ifcname;
struct rip_authenticate *entry;
{
struct rip_auth *rd;
for (rd = auth; rd != NULLAUTH; rd = rd->next) {
if ((strcmp(ifcname, rd->ifc_name) == 0) ||
(strcmp(DEFAULTIFC, rd->ifc_name) == 0)) {
if (rd->domain == header->rip_domain) {
/* We'll take this domain, check against a NULL password */
if (strcmp(rd->rip_auth_code, RIP_NO_AUTH) == 0) {
rip_trace(3, "RIP-2 taken due to no password from [%s] domain %d",
inet_ntoa(srcaddr), header->rip_domain);
return(TRUE);
} else {
/* Okay, we need an authentication */
if (entry->rip_family != RIP_AF_AUTH) {
/* It doesn't have an authentication packet */
rip_trace(2, "RIP-2 lacking authentication packet from [%s] domain %d",
inet_ntoa(srcaddr), header->rip_domain);
Rip_stat.auth_fail++;
return(FALSE);
}
if (entry->rip_auth_type != RIP_AUTH_SIMPLE) {
/* Only support simple authentication */
rip_trace(2, "RIP-2 wrong type of authentication from [%s]",
inet_ntoa(srcaddr));
Rip_stat.auth_fail++;
return(FALSE);
}
if (memcmp(rd->rip_auth_code,entry->rip_auth_str,RIP_AUTH_SIZE) == 0) {
rip_trace(3, "RIP-2 authenticated from [%s] domain %d",
inet_ntoa(srcaddr), header->rip_domain);
return(TRUE);
} else {
rip_trace(2, "RIP-2 authentication failed from [%s] domain %d,\n attempted password '%.16s' right password '%.16s'",
inet_ntoa(srcaddr), header->rip_domain, entry->rip_auth_str, rd->rip_auth_code);
Rip_stat.auth_fail++;
return(FALSE);
}
}
}
}
}
/* Didn't find the right routing domain for this packet */
rip_trace(3, "RIP-2 domain %d not accepted from [%s]", header->rip_domain,
inet_ntoa(srcaddr));
Rip_stat.wrong_domain++;
return(FALSE);
}
/* Route timeout handler. If route has already been marked for deletion
* then delete it. Otherwise mark for deletion and restart timer.
*/
void
rt_timeout(s)
void *s;
{
register struct route *rp = (struct route *)s;
stop_timer(&rp->timer);
if(rp->metric < RIP_METRIC_UNREACHABLE){
rip_trace(5, "RIP: route to [%s]/%d expired - hold down", inet_ntoa(rp->target),
rp->bits);
rp->metric = RIP_METRIC_UNREACHABLE;
if(dur_timer(&rp->timer) == 0)
set_timer(&rp->timer,Rip_ttl*1000L);
/* wait 2/3 of timeout before garbage collect */
set_timer(&rp->timer,dur_timer(&rp->timer)*2/3);
rp->timer.func = (void *)rt_timeout;
rp->timer.arg = (void *)rp;
start_timer(&rp->timer);
/* Route changed; mark it for triggered update */
rp->flags |= RTTRIG;
rip_trigger();
} else {
rip_trace(5, "RIP: route to [%s]/%d expired - dropped", inet_ntoa(rp->target),
rp->bits);
rt_drop(rp->target,rp->bits);
}
}
void
pullheader(ep,bpp)
struct rip_head *ep;
struct mbuf **bpp;
{
ep->rip_cmd = pullchar(bpp);
ep->rip_vers = pullchar(bpp);
ep->rip_domain = pull16(bpp);
}
void
pullauthentication(ep,bpp)
struct rip_authenticate *ep;
struct mbuf **bpp;
{
int x;
ep->rip_family = pull16(bpp);
ep->rip_auth_type = pull16(bpp);
for (x = 0; x < RIP_AUTH_SIZE; x++)
ep->rip_auth_str[x] = pullchar(bpp);
}
void rip_trace(short level, char *errstr, ...)
{
if (level <= Rip_trace) {
char *timestr;
time_t timer;
va_list argptr;
if (Rip_trace_fname != NULLCHAR) {
time(&timer);
timestr = ctime(&timer);
timestr[24] = '\0';
Rip_trace_file = fopen(Rip_trace_fname, APPEND_TEXT);
fprintf(Rip_trace_file, "%s - ", timestr);
va_start(argptr, errstr);
vfprintf(Rip_trace_file, errstr, argptr);
va_end(argptr);
fprintf(Rip_trace_file, "\n");
fclose(Rip_trace_file);
} else {
va_start(argptr, errstr);
usvprintf(Curproc->output, errstr, argptr);
va_end(argptr);
tprintf("\n");
}
}
}
void
pullentry(ep,bpp)
register struct rip_route *ep;
struct mbuf **bpp;
{
ep->rip_family = pull16(bpp);
ep->rip_tag = pull16(bpp);
ep->rip_dest = pull32(bpp);
ep->rip_dest_mask = pull32(bpp);
ep->rip_router = pull32(bpp);
ep->rip_metric = pull32(bpp);
}
#endif /* RIP */